package com.chunyu.app.utils;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.UUID;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSONObject;

public class MyHttpUrlClient {
	private static final int TIME_OUT = 1024 * 1000;
	private static final String CHARSET = "UTF-8";
	private static final String PREFIX = "--";
	private static final String MULTIPART_CONTENT_TYPE = "multipart/form-data";
	private static final String LINE_END = "\r\n";
	public Logger logger = LoggerFactory.getLogger(getClass());
	private String sessionId = null;
	public HttpURLConnection httpURLConnection = null;

	public MyHttpUrlClient() {
		super();
	}

	public MyHttpUrlClient(String sessionId) {
		super();
		this.sessionId = sessionId;
	}

	public String getSessionId() {
		return sessionId;
	}

	public void setSessionId(String sessionId) {
		this.sessionId = sessionId;
	}

	/**
	 * 解析URL传递带的参数
	 * 
	 * @param urlStr
	 * @return
	 */
	public Map<String, String[]> parseUrlParams(String urlStr) {
		if (urlStr.indexOf("?") < 0) {
			return new HashMap<String, String[]>();
		}
		String[] params = urlStr.split("\\?");
		String valArray[] = null;
		Map<String, String[]> ht = new HashMap<String, String[]>();
		StringTokenizer st = new StringTokenizer(params[1], "&");
		while (st.hasMoreTokens()) {
			String pair = (String) st.nextToken();
			int pos = pair.indexOf('=');
			if (pos == -1) {
				continue;
			}
			String key = pair.substring(0, pos);
			String val = pair.substring(pos + 1, pair.length());
			if (ht.containsKey(key)) {
				String oldVals[] = (String[]) ht.get(key);
				valArray = new String[oldVals.length + 1];
				for (int i = 0; i < oldVals.length; i++) {
					valArray[i] = oldVals[i];
				}
				valArray[oldVals.length] = val;
			} else {
				valArray = new String[1];
				valArray[0] = val;
			}
			ht.put(key, valArray);
		}
		return ht;
	}

	/**
	 * 参数转换函数 map -> http[post] 参数
	 * 
	 * @param form_data
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public String createFormData(Map<?, ?> formData, String prefix) throws UnsupportedEncodingException {
		StringBuilder urlForm = new StringBuilder();
		if (NullUtils.isEmpty(formData)) {
			urlForm.append("t=").append(System.currentTimeMillis());
		} else {
			// 遍历map，按照url参数形式拼接
			for (Object key : formData.keySet()) {
				urlForm.append(urlForm.length() == 0 ? "" : "&");
				Object obj = formData.get(key);
				if (NullUtils.isNotEmpty(obj)) {
					try {
						if (obj instanceof Date) {
							urlForm.append(NullUtils.isEmpty(prefix) ? "" : prefix + ".").append(key).append("=").append(URLEncoder.encode(DateUtils.date2DashYyyyMMddHHmmssStr((Date) obj), CHARSET));
						} else if (!(obj instanceof Collection || obj instanceof Map)) {
							urlForm.append(NullUtils.isEmpty(prefix) ? "" : prefix + ".").append(key).append("=").append(URLEncoder.encode(obj.toString(), CHARSET));
						}
						if (obj instanceof Map && NullUtils.isNotEmpty(obj)) {
							urlForm.append(createFormData((Map<?, ?>) obj, key.toString()));
						}
						if (obj instanceof Collection && NullUtils.isNotEmpty(obj)) {
							int index = 0;
							for (Object subObj : (Collection<?>) obj) {
								urlForm.append(createFormData(BeanUtils.toMap(subObj), key.toString() + "[" + index + "]"));
								index++;
							}
						}
					} catch (Exception e) {
						logger.error("key=" + obj.toString(), e);
					}
				}
			}
		}
		return urlForm.toString();

	}

	/**
	 * 对post参数进行编码处理,带文件处理
	 */
	public String createFormMultipartData(Map<?, ?> formData, String boundary, String prefix) {
		StringBuilder strSb = new StringBuilder();
		for (Object key : formData.keySet()) {
			Object obj = formData.get(key);
			if (!(obj instanceof File)) {
				strSb.append(PREFIX).append(boundary).append(LINE_END);
				strSb.append("Content-Disposition: form-data; name=\"" + (NullUtils.isEmpty(prefix) ? key.toString() : prefix + "." + key.toString()) + "\"" + LINE_END);
				strSb.append("Content-Type: text/plain; charset=" + CHARSET + LINE_END).append("Content-Transfer-Encoding: 8bit" + LINE_END).append(LINE_END);
				// 参数头设置完以后需要两个换行，然后才是参数内容
				if (obj instanceof Date) {
					strSb.append(DateUtils.date2DashYyyyMMddHHmmssStr((Date) obj));
				} else if (!(obj instanceof Collection || obj instanceof Map)) {
					strSb.append(obj.toString());
				}
				strSb.append(LINE_END);
				if (obj instanceof Map && NullUtils.isNotEmpty(obj)) {
					strSb.append(createFormMultipartData((Map<?, ?>) obj, boundary, key.toString()));
				}
				if (obj instanceof Collection && NullUtils.isNotEmpty(obj)) {
					int index = 0;
					for (Object subObj : (Collection<?>) obj) {
						strSb.append(createFormMultipartData(BeanUtils.toMap(subObj), boundary, key.toString() + "[" + index + "]"));
						index++;
					}
				}
			}
		}
		return strSb.toString();
	}

	/**
	 * 关闭相关资源
	 * 
	 * @param closeable
	 */
	public void closeQuietly(final Closeable closeable) {
		if (closeable == null) {
			return;
		}
		try {
			closeable.close();
		} catch (Exception exc) {

		}
	}

	/**
	 * 关闭相关资源
	 * 
	 * @param closeable
	 */
	public void disconnect(final HttpURLConnection httpURLConnection) {
		if (httpURLConnection == null) {
			return;
		}
		try {
			httpURLConnection.disconnect();
		} catch (Exception exc) {

		}
	}

	public void parseSession(final HttpURLConnection httpURLConnection) {
		// 打印请求头信息
		String cookieValue = httpURLConnection.getHeaderField("Set-Cookie");
		if (NullUtils.isNotEmpty(cookieValue)) {
			this.sessionId = cookieValue.substring(0, cookieValue.indexOf(";"));
		}
	}

	protected void handleHttpResponseStatusCode() throws Exception {
		String msg = getCause(httpURLConnection.getResponseCode()) + "\n";
		switch (httpURLConnection.getResponseCode()) {
		case 200:
		case 201:
		case 202:
		case 304:
			break;
		case 400:
		case 404:
		case 406:
			throw new Exception(msg + this.httpURLConnection.getResponseMessage());
		case 401:
			throw new Exception(msg + httpURLConnection.getResponseMessage());
		case 403:
			throw new Exception(msg);
		case 500:
		case 502:
		case 503:
			throw new Exception(msg);
		default:
			throw new Exception(msg + httpURLConnection.getResponseMessage());
		}
	}

	private static String getCause(int statusCode) {
		String cause = null;
		switch (statusCode) {
		case 304:
			break;
		case 400:
			cause = "无效请求";
			break;
		case 401:
			cause = "非法用户";
			break;
		case 403:
			cause = "请求被拒绝";
			break;
		case 404:
			cause = "请求地址不存在.";
			break;
		case 406:
			cause = "数据格式不正确";
			break;
		case 500:
			cause = "内部错误";
			break;
		case 502:
			cause = "服务处于维护状态";
			break;
		case 503:
			cause = "服务不可访问";
			break;
		default:
			cause = "";
		}
		return statusCode + ":" + cause;
	}

	public String readResponse(HttpURLConnection httpURLConnection) throws Exception {
		BufferedReader bufferedReader = null;
		String line = null;
		StringBuilder response = new StringBuilder();
		handleHttpResponseStatusCode();
		// 读取服务器返回信息,获取响应 输入流
		try {
			if (httpURLConnection.getResponseCode() == 200) {
				bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
				while ((line = bufferedReader.readLine()) != null) {
					response.append(line);
				}
				return response.toString().trim();
			}
		} catch (IOException e) {
			logger.error("read response error");
			throw e;
		} finally {
			this.closeQuietly(bufferedReader);
		}
		return "";
	}

	public void createHttpURLConnection(String urlStr) throws IOException {
		if (NullUtils.isNotEmpty(httpURLConnection)) {
			this.disconnect(httpURLConnection);
		}
		// 使用该地址创建一个 URL 对象
		URL url = new URL(urlStr);
		// 使用创建的URL对象的openConnection()方法创建一个HttpURLConnection对象
		httpURLConnection = (HttpURLConnection) url.openConnection();
		httpURLConnection.setDoOutput(true);
		httpURLConnection.setDoInput(true);
		httpURLConnection.setUseCaches(false);
		httpURLConnection.setReadTimeout(TIME_OUT);
		httpURLConnection.setConnectTimeout(TIME_OUT);
		httpURLConnection.setRequestMethod("POST");
		httpURLConnection.setRequestProperty("Connection", "Keep-Alive");
		httpURLConnection.setRequestProperty("Charset", CHARSET);
		httpURLConnection.setRequestProperty("Accept-Encoding", "identity");
		httpURLConnection.setRequestProperty("X-Requested-With", "XMLHttpRequest");
		httpURLConnection.setRequestProperty("User-Agent", " Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.15 Safari/537.36");
		if (NullUtils.isNotEmpty(this.sessionId)) {
			int index = this.sessionId.indexOf("=");
			httpURLConnection.setRequestProperty("Cookie", "JSESSIONID=" + (index > 0 ? this.sessionId.substring(index + 1) : this.sessionId));
		}
	}

	public String post(String urlStr, Map<String, Object> params) {
		String body = "";
		PrintWriter pw = null;
		try {
			createHttpURLConnection(urlStr);
			// application/x-www-form-urlencoded 默认的提交方式，
			// 同GET类似，将参数组装成Key-value方式，用&分隔，但数据存放在body中提交
			httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + CHARSET);
			// 获取连接
			httpURLConnection.connect();
			// 获取连接输出流，并写入表单参数
			pw = new PrintWriter(httpURLConnection.getOutputStream());
			pw.print(createFormData(params, null));
			pw.flush();
			// 获取响应 输入流
			parseSession(httpURLConnection);
			body = readResponse(httpURLConnection);
		} catch (Exception e) {
			logger.error("post", e);
		} finally {
			this.closeQuietly(pw);
			this.disconnect(httpURLConnection);
		}
		return body;
	}

	/**
	 * 提交Json数据
	 * 
	 * @param urlStr
	 * @param object
	 * @param encoding
	 * @return
	 */
	public String postJSON(String urlStr, Object object) {
		String body = "";
		OutputStreamWriter writer = null;
		try {
			createHttpURLConnection(urlStr);
			httpURLConnection.setRequestProperty("Content-Type", "application/json;charset=" + CHARSET);
			// 获取连接
			httpURLConnection.connect();

			// 获取连接输出流，并写入表单参数
			writer = new OutputStreamWriter(httpURLConnection.getOutputStream());
			writer.write(JSONObject.toJSONString(object));
			writer.flush();
			// 获取响应 输入流
			parseSession(httpURLConnection);
			body = readResponse(httpURLConnection);
		} catch (Exception e) {
			logger.error("postJSON", e);
		} finally {
			this.closeQuietly(writer);
			this.disconnect(httpURLConnection);
		}
		return body;
	}

	private void uploadFile(DataOutputStream dos, File file) throws IOException {
		InputStream is = null;
		byte[] buffer = new byte[1024];
		int len = 0;
		try {
			is = new FileInputStream(file);
			while ((len = is.read(buffer)) != -1) {
				dos.write(buffer, 0, len);
			}
		} catch (IOException e) {
			throw e;
		} finally {
			this.closeQuietly(is);
		}
	}

	/**
	 * 提交Json数据
	 * 
	 * @param urlStr
	 * @param object
	 * @param encoding
	 * @return
	 */
	public String upload(String urlStr, Map<String, Object> params) {
		String boundary = UUID.randomUUID().toString();
		String body = "";
		DataOutputStream dos = null;
		OutputStreamWriter writer = null;
		try {
			createHttpURLConnection(urlStr);
			httpURLConnection.setRequestProperty("Content-Type", MULTIPART_CONTENT_TYPE + ";boundary=" + boundary);
			// 获取连接
			httpURLConnection.connect();
			// 请求体 上传参数
			dos = new DataOutputStream(httpURLConnection.getOutputStream());
			// getStrParams()为一个
			dos.write(createFormMultipartData(params, boundary, null).getBytes(CHARSET));
			dos.flush();

			// 文件上传
			StringBuilder fileSb = new StringBuilder();
			for (String key : params.keySet()) {
				Object obj = params.get(key);
				if (obj instanceof File) {
					File file = (File) obj;
					fileSb.append(PREFIX).append(boundary).append(LINE_END);
					// name里面的值为服务端需要的key 只有这个key 才可以得到对应的文件
					// filename是文件的名字，包含后缀名的 比如:abc.png
					fileSb.append("Content-Disposition: form-data; name=\"").append(key).append("\"; filename=\"" + file.getName() + "\"" + LINE_END);
					// 此处的ContentType不同于请求头中Content-Type
					fileSb.append("Content-Type: application/octet-stream" + LINE_END);
					// 参数头设置完以后需要两个换行，然后才是参数内容
					fileSb.append("Content-Transfer-Encoding: 8bit" + LINE_END).append(LINE_END);
					dos.write(fileSb.toString().getBytes(CHARSET));
					dos.flush();
					uploadFile(dos, file);
					dos.writeBytes(LINE_END);
				}
			}
			// 请求结束标志
			dos.write((PREFIX + boundary + PREFIX + LINE_END).getBytes(CHARSET));
			dos.flush();
			dos.close();
			parseSession(httpURLConnection);
			body = readResponse(httpURLConnection);

		} catch (Exception e) {
			logger.error("post", e);
		} finally {
			this.closeQuietly(writer);
			this.closeQuietly(dos);
			this.disconnect(httpURLConnection);
		}
		return body;
	}

	/**
	 * 获取文件下载的文件名
	 * 
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public String getFileName() throws UnsupportedEncodingException {

		String contentDisposition = URLDecoder.decode(httpURLConnection.getHeaderField("content-Disposition"), CHARSET);
		if (!NullUtils.isEmpty(contentDisposition)) {
			String[] segments = contentDisposition.split("\\;");
			for (String segment : segments) {
				String[] keyValues = segment.split("\\=");
				if (keyValues.length == 2 && keyValues[0].equalsIgnoreCase("fileName")) {
					return keyValues[1];
				}
			}
		}
		return "";
	}

	/**
	 * 获取文件下载的长度
	 * 
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public Long getFileLength() throws UnsupportedEncodingException {
		// 文件名
		String length = URLDecoder.decode(httpURLConnection.getHeaderField("Content-Length"), CHARSET);
		if (NullUtils.isEmpty(length)) {
			return Long.valueOf(httpURLConnection.getContentLength());
		}
		return Long.valueOf(length);
	}

	/**
	 * 从服务器下载文件
	 * 
	 * @param urlStr
	 * @param params
	 * @param downloadDir
	 * @return
	 * @throws IOException
	 */
	public File download(String urlStr, Map<String, Object> params, String downloadDir) throws IOException {
		File file = null;
		PrintWriter pw = null;
		BufferedInputStream bin = null;
		OutputStream out = null;
		try {
			createHttpURLConnection(urlStr);
			httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			// 获取连接
			httpURLConnection.connect();
			// 获取连接输出流，并写入表单参数
			pw = new PrintWriter(httpURLConnection.getOutputStream());
			pw.print(createFormData(params, null));
			pw.flush();
			this.closeQuietly(pw);
			// 获取响应 输入流

			bin = new BufferedInputStream(httpURLConnection.getInputStream());
			String path = downloadDir + File.separator + getFileName();
			file = new File(path);
			if (!file.getParentFile().exists()) {
				file.getParentFile().mkdirs();
			}
			if (!file.exists()) {
				file.createNewFile();
			}
			out = new FileOutputStream(file);
			int size = 0;
			byte[] buf = new byte[1024];
			while ((size = bin.read(buf)) != -1) {
				out.write(buf, 0, size);
			}
			this.closeQuietly(bin);
			this.closeQuietly(out);
			parseSession(httpURLConnection);
			return file;
		} catch (IOException e) {
			logger.error("download", e);
			throw e;
		} finally {
			this.closeQuietly(pw);
			this.closeQuietly(bin);
			this.closeQuietly(out);
		}
	}

	public void download(String urlStr, Map<String, Object> params, File file) throws IOException {
		FileOutputStream out = null;
		try {
			if(! file.getParentFile().exists()){
				file.getParentFile().mkdirs();
			}
			out = new FileOutputStream(file);
			this.download(urlStr, params, out);
		} catch (IOException e) {
			logger.error("download", e);
			throw e;
		} finally {
			this.closeQuietly(out);
		}
	}

	/**
	 * 从服务器下载文件
	 * 
	 * @param urlStr
	 * @param params
	 * @param downloadDir
	 * @return
	 * @throws IOException
	 */
	public void download(String urlStr, Map<String, Object> params, OutputStream out) throws IOException {
		PrintWriter pw = null;
		BufferedInputStream bin = null;
		try {
			createHttpURLConnection(urlStr);
			httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			// 获取连接
			httpURLConnection.connect();
			// 获取连接输出流，并写入表单参数
			pw = new PrintWriter(httpURLConnection.getOutputStream());
			pw.print(createFormData(params, null));
			pw.flush();
			this.closeQuietly(pw);
			// 获取响应 输入流
			bin = new BufferedInputStream(httpURLConnection.getInputStream());
			int size = 0;
			byte[] buf = new byte[1024];
			while ((size = bin.read(buf)) != -1) {
				out.write(buf, 0, size);
			}
			this.closeQuietly(bin);
			this.closeQuietly(out);
			parseSession(httpURLConnection);
		} catch (IOException e) {
			logger.error("download", e);
			throw e;
		} finally {
			this.closeQuietly(pw);
			this.closeQuietly(bin);
		}
	}

	/**
	 * 从服务器下载文件
	 * 
	 * @param urlStr
	 * @param params
	 * @param downloadDir
	 * @return
	 * @throws IOException
	 */
	public InputStream getDownloadStream(String urlStr, Map<String, Object> params) throws IOException {
		PrintWriter pw = null;
		try {
			createHttpURLConnection(urlStr);
			this.httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			// 获取连接
			this.httpURLConnection.connect();
			// 获取连接输出流，并写入表单参数
			pw = new PrintWriter(this.httpURLConnection.getOutputStream());
			pw.print(createFormData(params, null));
			pw.flush();
			this.closeQuietly(pw);
			// 获取响应 输入流
			return this.httpURLConnection.getInputStream();

		} catch (IOException e) {
			logger.error("download", e);
			throw e;
		}
	}
}
